<!--
  Copyright 2016 The LUCI Authors. All rights reserved.
  Use of this source code is governed under the Apache License, Version 2.0
  that can be found in the LICENSE file.
  -->

<link rel="import" href="../bower_components/polymer/polymer.html">

<link rel="import" href="rpc-code.html">
<link rel="import" href="rpc-error.html">
<link rel="import" href="rpc-call.html">

<!--
  The `rpc-client` element can send a RPC request.
  Supports pRPC.
  Protocol: https://godoc.org/go.chromium.org/luci/common/prpc#hdr-Protocol
-->
<dom-module id="rpc-client">
  <script>
    'use strict';

    Polymer({
      is: 'rpc-client',

      properties: {
        /**
         * If true, use HTTP instead of HTTPS.
         * If null or undefined (default), determined automatically:
         * - If host equals current host and current protocol is http, then
         *   false.
         * - otherwise true.
         */
        insecure: {
          type: Boolean,
          value: null
        },

        /**
         * pRPC server host, defaults to current document host.
         */
        host: {
          type: String,
          value: document.location.host,
        },

        /**
         * Full service name, including package name.
         */
        service: String,

        /**
         * Service method name.
         */
        method: String,

        /**
         * Request object.
         */
        request: {
          type: Object,
          value: function() {
            return {};
          }
        },

        /**
         * Request timeout in milliseconds.
         */
        timeout: {
          type: Number,
          value: 0,
        },

        /**
          * OAuth 2.0 access token to use in RPC.
          */
        accessToken: {
          type: String,
          value: null
        },

        /**
         * If true, automatically sends a request when host, service, method,
         * request or accessToken changes.
         */
        auto: {
          type: Boolean,
          value: false
        },

        /**
         * If true, error messages will automatically be logged to the console.
         */
        verbose: {
          type: Boolean,
          value: false
        },

        /**
         * The most recent call made by this client.
         */
        lastCall: {
          type: Object,
          notify: true,
          readOnly: true
        },

        /**
         * True while lastCall is in flight.
         */
        loading: {
          type: Boolean,
          notify: true,
          readOnly: true,
          value: false
        },

        /**
         * lastCall's response.
         *
         * Note that lastResponse, lastCode and lastError are set when
         * lastCall finishes, so if loading is true, they will correspond to the
         * result of the previous request.
         */
        lastResponse: {
          type: Object,
          notify: true,
          readOnly: true
        },

        /**
         * lastCall's response code.
         */
        lastCode: {
          type: Number,
          notify: true,
          readOnly: true
        },

        /**
         * lastCall's error, if any.
         * May be an instance of luci.rpc.GrpcError which has response code
         * and description.
         */
        lastError: {
          type: Number,
          notify: true,
          readOnly: true
        },

        /**
         * Length of time in milliseconds to debounce multiple automatically
         * generated requests.
         */
        debounceDuration: {
          type: Number,
          value: 0,
          notify: true
        }
      },

      observers: [
        ('_callOptionsChanged(' +
          'host, service, method, request.*, accessToken, auto)')
      ],

      _canCall: function () {
        return this.host && this.service && this.method;
      },

      _callOptionsChanged: function() {
        this.debounce('call', function() {
          if (this.auto && this._canCall()) {
            this.call();
          }
        }, this.debounceDuration);
      },

      /**
        * Send an RPC request.
        * @return {!RpcCallElement}
        */
      call: function() {
        var call = document.createElement('rpc-call');
        call.send({
          insecure: this.insecure,
          host: this.host,
          service: this.service,
          method: this.method,
          request: this.request,
          timeout: this.timeout,
          accessToken: this.accessToken
        });
        this._setLastCall(call);

        call.completes.then(
            this._onResponse.bind(this)
        ).catch(
            this._onError.bind(this, call)
        );

        this._setLoading(true);
        this.fire('request', {call: call}, {bubbles: false});
        return call;
      },

      _onError: function(call, error) {
        this._onCallComplete(call);
        if (this.verbose) {
          console.error(error);
        }
        this.fire('error', {
          call: call,
          error: error
        }, {bubbles: false});
      },

      _onResponse: function(call) {
        this._onCallComplete(call);
        this.fire('response', {call: call}, {bubbles: false});
      },

      _onCallComplete: function(call) {
        if (call === this.lastCall) {
          this._setLastError(call.error);
          this._setLastResponse(call.response);
          this._setLastCode(call.code);
          this._setLoading(false);
        }
      }

    });
  </script>
</dom-module>
